package com.ld.admin.websocket.chat;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;

import javax.websocket.EndpointConfig;
import javax.websocket.RemoteEndpoint.Async;
import javax.websocket.Session;

import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.reflect.ModelUtil;
import com.ld.shieldsb.common.core.util.DateUtil;
import com.ld.shieldsb.common.core.util.properties.PropertiesPojo;
import com.ld.shieldsb.common.core.util.useragent.UserAgentModel;
import com.ld.shieldsb.common.core.util.useragent.UserAgentUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class WebSocketUtil {

    /**
     * 获取默认ip
     * 
     * @Title getDefaultRemoteAddress
     * @author 吕凯
     * @date 2019年2月28日 下午1:52:25
     * @param session
     * @return String
     */
    public static String getDefaultRemoteAddress(Session session) {
        if (session == null) {
            return null;
        }
        Async async = session.getAsyncRemote();

        // 在Tomcat 8.0.x版本有效
//      InetSocketAddress addr = (InetSocketAddress) getFieldInstance(async,"base#sos#socketWrapper#socket#sc#remoteAddress");
        // 在Tomcat 8.5以上版本有效
        InetSocketAddress addr = (InetSocketAddress) getFieldInstance(async, "base#socketWrapper#socket#sc#remoteAddress");
        log.warn("==========ws ip:" + addr + "");
        return addr.getAddress().getHostAddress();
    }

    /**
     * 获取远程地址（包含nginx等反向代理的）
     * 
     * @Title getRemoteAddress
     * @author 吕凯
     * @date 2019年2月28日 下午1:52:51
     * @param session
     * @param headers
     * @return String
     */
    public static String getRemoteAddress(Session session, Map<String, List<String>> headers) {
        // 参考 https://blog.csdn.net/w820896059/article/details/86082865
        String defaultIP = getDefaultRemoteAddress(session);
        // 针对于nginx多级反向代理情况
        String ip = getHeader(headers, "nkforwardfor"); // 可伪造，需要在nginx中设置：proxy_set_header nkforwardfor $proxy_add_x_forwarded_for;
        if (ipIsCanDeal(ip)) {
            String[] ipStrs = ip.split(",");
            for (String ipStr : ipStrs) {
                if (!"unknown".equalsIgnoreCase(ipStr)) { // 取第一个不为unknown的值
                    ip = ipStr;
                    break;
                }
            }
            String ipWl = getHeader(headers, "WL-Proxy-Client-IP"); // 其中“WL” 就是 WebLogic
            if (ipWl != null && ipWl.length() != 0 && !"unknown".equalsIgnoreCase(ipWl) && !ipWl.equals(ip)) { // 优先取
                log.info("ip异常:" + ip + " != (wl)" + ipWl);
                ip = ipWl;
            }
        } else {
            // 针对于nginx反向代理情况
            ip = getHeader(headers, "nkrealip"); // 可伪造，需要在nginx中设置：proxy_set_header nkrealip $remote_addr;
            // 针对于正常情况
            if (!ipIsCanDeal(ip)) {
                ip = defaultIP;
            }
        }
        if (!ipIsLegal(ip)) {
            ip = defaultIP;
        }
        return ip;
    }

    /**
     * 判断ip是否合法
     * 
     * @author lv
     * @param ip
     * @return
     */
    private static boolean ipIsLegal(String ip) {
        // ipv6未严格判断
        return ip.matches("\\d+\\.\\d+\\.\\d+\\.\\d+") || (ip.contains(":") && !ip.matches(".*?[\\s，,\\.].*?"));
    }

    /**
     * 判断ip是否可以解析
     * 
     * @author lv
     * @param ip
     * @return
     */
    private static boolean ipIsCanDeal(String ip) {
        return ip != null && ip.length() > 0 && !"unknown".equalsIgnoreCase(ip);
    }

    /**
     * 获取header中的信息
     * 
     * @Title getHeader
     * @author 吕凯
     * @date 2019年2月28日 下午1:53:29
     * @param headers
     * @param key
     * @return String
     */
    public static String getHeader(Map<String, List<String>> headers, String key) {
        if (headers == null || key == null) {
            return null;
        }
        List<String> valueList = headers.get(key);
        if (ListUtils.isNotEmpty(valueList)) {
            return valueList.get(0);
        }
        return null;
    }

    private static Object getFieldInstance(Object obj, String fieldPath) {
        String fields[] = fieldPath.split("#");
        for (String field : fields) {
            obj = ModelUtil.getModelValue(obj, field);
            if (obj == null) {
                return null;
            }
        }

        return obj;
    }

    /**
     * 获取cofig中的headers
     * 
     * @Title getHeaders
     * @author 吕凯
     * @date 2019年2月28日 下午1:50:44
     * @param config
     * @return Map<String,List<String>>
     */
    @SuppressWarnings("unchecked")
    public static Map<String, List<String>> getHeaders(EndpointConfig config) {
        return (Map<String, List<String>>) config.getUserProperties().get("headers");
    }

    public static String getClientInfo(Map<String, List<String>> headers) {
        UserAgentModel userAgentModel = UserAgentUtil.getUserAgent(WebSocketUtil.getHeader(headers, "user-agent"));
        if (userAgentModel != null) {
            String clientInfo = userAgentModel.getBrowserType() == null ? "未知" : userAgentModel.getBrowserType();
            if (userAgentModel.getBrowserVersion() == null) {
                clientInfo += userAgentModel.getBrowserVersion();
            }
            return clientInfo;
        }
        return null;
    }

    /**
     * 设置websocket容器是否是内置
     * 
     * @Title setTomcatEmbed
     * @author 吕凯
     * @date 2019年2月28日 上午8:31:34
     * @param embed
     *            void
     */
    public static void setWSTomcatEmbed(boolean embed) {
        PropertiesPojo wsTomcat = PropertiesModel.CONFIG.getProperty("shieldsb.websocket.tomcatEmbed");
        String defaultVal = embed ? "true" : "false";
        String webContainer = embed ? "内置容器" : "外部tomcat";
        if (!defaultVal.equals(wsTomcat.getValue())) {
            String comment = wsTomcat.getComment() != null ? wsTomcat.getComment() : ",";
            comment += "检测为" + webContainer + "系统自动修改(" + DateUtil.getNowDateTime() + ")";
            PropertiesModel.CONFIG.save("shieldsb.websocket.tomcatEmbed", defaultVal, comment);
        }
    }

// 单独使用时，使用此方法
//    private static Object getField(Object obj, Class<?> clazz, String fieldName) {
//        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
//            try {
//                Field field;
//                field = clazz.getDeclaredField(fieldName);
//                field.setAccessible(true);
//                return field.get(obj);
//            } catch (Exception e) {
//            }
//        }
//
//        return null;
//    }

}
